Part II Advanced displays
Preparation
You will find the material for this lecture at https://github.com/cbhurley/CRT2021vis
First install these packages
install.packages(c("GGally", "ISLR2", "naniar"))
and load with
library(GGally)
library(ISLR2)
library(timetk) # for the data
library(palmerpenguins)
library(tidyverse)
library(naniar)
bike <- bike_sharing_daily
bike$season <- c("Winter","Spring", "Summer","Fall")[bike$season]
bike$season <- factor(bike$season, levels = c("Winter","Spring", "Summer","Fall"))
bike$mnth <- factor(bike$mnth)
bike$holiday <- factor(c("No","Yes")[bike$holiday+1])
bike$workingday <- factor(c("No","Yes")[bike$workingday+1])
bike$yr <- factor(c("2011","2012")[bike$yr+1])
bike$weathersit <- c("clear", "cloudy", "lightP", "heavyP")[bike$weathersit]
bike$weathersit <- factor(bike$weathersit, levels= c("clear", "cloudy", "lightP", "heavyP"))
bike <- droplevels(bike)
Big data
ISLR2 has an hourly version of the bike data with 8645 observations.
The plot of registered versuscasual users is
ggplot(data=Bikeshare, aes(x=casual, y=registered)) + geom_point()

There is likely lot of overplotting.
Jittering will randomly undo rounding, and using alpha will help too
ggplot(data=Bikeshare, aes(x=casual, y=registered)) +
geom_jitter(alpha=.5, size=.5)

Other options are to form 2d bins, count the number of observations in each, and color in proportion to the frequency.
p <- ggplot(data=Bikeshare, aes(x=casual, y=registered))
p+ geom_bin2d()

I prefer to make my colour scale light to dark:
p +
geom_bin2d() +
scale_fill_gradient(low = "lightblue1", high = "steelblue4")

p +
geom_bin2d() +
scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

The second plot takes a log of the counts so you get a better spread of colours
p +
stat_binhex() +
scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")
stat_binhex uses hexagonal binning
A more advanced plot fits a 2d density estimate and draw this as a contour plot.
p + stat_density2d()

# fills in the contours
p + stat_density2d(aes(fill = after_stat(level)), geom = "polygon")
Scatterplot matrix
Back to the smaller dataset.
Go to articles tab for info https://ggobi.github.io/ggally/
library(GGally)
ggpairs(bike, columns=c("casual","registered","cnt" ))

You can change what is plotted on lower, upper and diagonal,using lower= etc. Putting in mapping= gives densities for both years separately
ggpairs(bike, mapping = aes(color = yr),
columns=c("casual","registered","cnt" ),
lower = list(continuous = wrap("smooth", method="lm", se=F, alpha=.5)),
diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

ggpairs can handle factors as well. In this plot yrs is in as a plot variable, and as colour.
ggpairs(bike, mapping = aes(color = yr),
columns=c("casual","registered","cnt","yr"),
lower = list(continuous = wrap("smooth", method="lm", se=F, alpha=.5)),
diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

Parallel coordinates
These can be constructed with function ggparcoord from GGally.
But, a newer package called ggpcp is better.
install.packages("devtools")
remotes::install_github("yaweige/ggpcp", build_vignettes = TRUE)
Construction: with a sample of 20 bike observations



This is how you create the parallel coordinate plot in ggpcp
library(ggpcp)
ggplot(bike[s,], aes(color=yr)) + geom_pcp(aes(vars=vars(casual,registered, cnt)))

Here, all variables are scaled to the unit interval individually. Variables that are positively correlated will have parallel line segments, here registered and cnt.
Other scaling options are available eg
ggplot(bike[s,], aes(color=yr)) + geom_pcp(aes(vars=vars(casual,registered, cnt)), method="raw")
In raw, no scaling is done. Here you can see that most users per day are registered.
To see what certain patterns look like in a parallel coordinate plot, let us look at some fake data
x <- rnorm(100)
y <- x+ .3*rnorm(100)
z <- -y+ .1*rnorm(100)
w <- -z+ .5*rnorm(100)
ggplot(data.frame(x,y,z,w))+ geom_pcp(aes(vars=vars(everything())))

Positive correlation exhibits as parallel segments.
Negative correlation has crossing. The stronger the negative correlation the smaller the pinch point.
ggplot(bike, aes(color=workingday)) + geom_pcp(aes(vars=vars(casual,registered, cnt)), alpha=.4)

In the above we see that there is a difference in the casual-registered association for workingday or not. Both groups have positive association but slopes are different. There are more casual users at the weekend.
Verifying with a scatterplot
ggplot(data=bike, aes(x=casual, y=registered, color=workingday)) + geom_point()

The same pattern was evident in the full bike data.
Standard parallel coordinate plots require numeric variables. There are variants that have been defined for categorical variables.
With ggpcp factor variables can be used as an axis variable:
ggplot(bike, aes(vars=vars( workingday,casual,registered, cnt), color=workingday)) +
geom_pcp(alpha=.4)

ggplot(bike, aes(vars=vars( workingday,casual,registered, cnt), color=workingday)) +
geom_pcp_box(boxwidth = 0.1, fill="grey70")+
geom_pcp(alpha=.4,boxwidth = 0.1)+
geom_pcp_text(boxwidth = 0.1)

Other variants of parallel coordinate plots for categorical data are in ggalluvial, ggparallel, but these are for factors only.
Practice 3
Using the penguins dataset, make a plot of the four dimension variables, colored by species.
Which pair of variables and species has the highest correlation?
Can you find two variables with negative correlation, but positive correlation within each species? This is called Simpson’s paradox.
Then make a parallel coordinate plot of the same four variables, with the same colouring scheme.
Can you identify presence of positive and negative correlation?
In this plot, how does the Gentoo species compare to the other two species?
Finally, make a parallel coordinate plot of species, island, and body_mass_g, coloured by species. How many species are on each island?
Missing data
Here we use package naniar. This is the dataset for your assignment. All the NAs are marked in grey.
m <- read_csv("marathon.csv",na=c(""," ","NA"))
vis_miss(m)

An upset plot shows the combination of missing values. Most of the NAs involve the club variable.
gg_miss_upset(m[,-c(22,20)])

Check out the visualisations in naniar for more options, eg the vignette at https://cran.r-project.org/web/packages/naniar/vignettes/naniar-visualisation.html
titanic case study
There were 2201 people aboard the titanic when it sank in 1912. A summary of the people on board and whether they survived or not is given in the titanicanic dataset.
Here are some images as it left its last port of call, Cobh.


Can we figure out from this data which category of passenger had the highest chance of survival?
This case study looks at 4 categorical variables simultaneously: - Survival (yes or no), - class (first, second, third, crew,) - gender (male or female) and - age (child or adult).
First we look at the number of survivors by class
#titanicanic # a 4d array....
titanic <- as.data.frame(Titanic)
head(titanic)
## Class Sex Age Survived Freq
## 1 1st Male Child No 0
## 2 2nd Male Child No 0
## 3 3rd Male Child No 35
## 4 Crew Male Child No 0
## 5 1st Female Child No 0
## 6 2nd Female Child No 0
titanic<- titanic[c(17:32,1:16),]
ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
geom_col()

and then the survival rates by class
ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
geom_col(position="fill")

- There is a big difference in Survival rates across the classes.
- First class passengers have the highest chance of survival, crew members the lowest.
We can bring in gender with facet_wrap
ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
geom_col(position="fill") +
facet_wrap( ~ Sex) + ylab("Rate")

Third class females do worse than crew females (46% versus 87%), and third class males do worse than crew males (17% versus 22%), although when gender is ignored the situation is reversed.
This apparent paradox (Simpson’s paradox) occurs because the vast majority of crew members are male.
The overall 3rd class survival rate of 25% is an average of 46% and 17%, but as there are 2.6 times more men than women in 3rd class 17% gets more weight.
The overall crew survival rate of 24% is an average of 87% and 22%, but as there are 37 times more men than women in crew class 87% gets very little weight.
Finally, bringing in age
ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
geom_col(position="fill") +
facet_wrap( ~ Age+ Sex) + ylab("Rate")

We can get some simplification by showing the survival rate, that is omit the red sections.
titanicR <-
titanic %>% group_by(Class, Sex,Age) %>%
summarise(died=Freq[2], survived=Freq[1], rate=survived/sum(Freq))
titanicR
## # A tibble: 16 × 6
## # Groups: Class, Sex [8]
## Class Sex Age died survived rate
## <fct> <fct> <fct> <dbl> <dbl> <dbl>
## 1 1st Male Child 0 5 1
## 2 1st Male Adult 118 57 0.326
## 3 1st Female Child 0 1 1
## 4 1st Female Adult 4 140 0.972
## 5 2nd Male Child 0 11 1
## 6 2nd Male Adult 154 14 0.0833
## 7 2nd Female Child 0 13 1
## 8 2nd Female Adult 13 80 0.860
## 9 3rd Male Child 35 13 0.271
## 10 3rd Male Adult 387 75 0.162
## 11 3rd Female Child 17 14 0.452
## 12 3rd Female Adult 89 76 0.461
## 13 Crew Male Child 0 0 NaN
## 14 Crew Male Adult 670 192 0.223
## 15 Crew Female Child 0 0 NaN
## 16 Crew Female Adult 3 20 0.870
ggplot(data = titanicR, aes(x = Class, y = rate)) +
geom_col(fill="lightblue") +
facet_wrap( ~ Age+ Sex)

One of the examples in the ggpcp vignette has a parallel coordinates version of this data: https://github.com/yaweige/ggpcp

Ci0tLQp0aXRsZTogIkRhdGEgdmlzdWFsaXNhdGlvbiBpbiBSIgphdXRob3I6CiAgLSBuYW1lOiAiQ2F0aGVyaW5lIEh1cmxleSIKICAgIGFmZmlsaWF0aW9uOiBNYXlub290aCBVbml2ZXJzaXR5CiAgICBlbWFpbDogY2F0aGVyaW5lLmh1cmxleUBtdS5pZQpkYXRlOiAiNy85LzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBwYW5kb2NfYXJnczogWyItLW51bWJlci1vZmZzZXQ9MSJdCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQogIAotLS0KCjwhLS0gPHN0eWxlIHR5cGU9InRleHQvY3NzIj4gLS0+CjwhLS0gICBib2R5LCB0ZCB7IC0tPgo8IS0tICAgICBmb250LXNpemU6IDE0cHQ7IC0tPgo8IS0tICAgfSAtLT4KPCEtLSBjb2RlLnJ7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0OyAtLT4KPCEtLSB9IC0tPgo8IS0tIHByZSB7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0IC0tPgo8IS0tIH0gLS0+CjwhLS0gPC9zdHlsZT4gLS0+CgoKCgoKCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RikKYGBgCiMgUGFydCBJSSBBZHZhbmNlZCBkaXNwbGF5cwoKIyMgUHJlcGFyYXRpb24KCllvdSB3aWxsIGZpbmQgdGhlIG1hdGVyaWFsIGZvciB0aGlzIGxlY3R1cmUgYXQgPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgoKRmlyc3QgaW5zdGFsbCB0aGVzZSBwYWNrYWdlcwoKYGBge3IsIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcyhjKCJHR2FsbHkiLCAiSVNMUjIiLCAibmFuaWFyIikpCmBgYAoKYW5kIGxvYWQgd2l0aAoKYGBge3J9CmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KElTTFIyKQpsaWJyYXJ5KHRpbWV0aykgIyBmb3IgdGhlIGRhdGEKbGlicmFyeShwYWxtZXJwZW5ndWlucykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobmFuaWFyKQoKYmlrZSA8LSBiaWtlX3NoYXJpbmdfZGFpbHkKYmlrZSRzZWFzb24gPC0gYygiV2ludGVyIiwiU3ByaW5nIiwgIlN1bW1lciIsIkZhbGwiKVtiaWtlJHNlYXNvbl0KYmlrZSRzZWFzb24gPC0gZmFjdG9yKGJpa2Ukc2Vhc29uLCBsZXZlbHMgPSBjKCJXaW50ZXIiLCJTcHJpbmciLCAiU3VtbWVyIiwiRmFsbCIpKSAgCgpiaWtlJG1udGggPC0gZmFjdG9yKGJpa2UkbW50aCkKCmJpa2UkaG9saWRheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJGhvbGlkYXkrMV0pCmJpa2Ukd29ya2luZ2RheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJHdvcmtpbmdkYXkrMV0pCgpiaWtlJHlyIDwtIGZhY3RvcihjKCIyMDExIiwiMjAxMiIpW2Jpa2UkeXIrMV0pCgpiaWtlJHdlYXRoZXJzaXQgPC0gYygiY2xlYXIiLCAiY2xvdWR5IiwgImxpZ2h0UCIsICJoZWF2eVAiKVtiaWtlJHdlYXRoZXJzaXRdCmJpa2Ukd2VhdGhlcnNpdCA8LSBmYWN0b3IoYmlrZSR3ZWF0aGVyc2l0LCBsZXZlbHM9IGMoImNsZWFyIiwgImNsb3VkeSIsICJsaWdodFAiLCAiaGVhdnlQIikpCmJpa2UgPC0gZHJvcGxldmVscyhiaWtlKQpgYGAKCgojIyBCaWcgZGF0YQoKSVNMUjIgaGFzIGFuIGhvdXJseSB2ZXJzaW9uIG9mIHRoZSBiaWtlIGRhdGEgd2l0aCA4NjQ1ICBvYnNlcnZhdGlvbnMuCgpUaGUgcGxvdCBvZiByZWdpc3RlcmVkIHZlcnN1c2Nhc3VhbCB1c2VycyBpcwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKVGhlcmUgaXMgbGlrZWx5IGxvdCBvZiBvdmVycGxvdHRpbmcuCgpKaXR0ZXJpbmcgd2lsbCByYW5kb21seSB1bmRvIHJvdW5kaW5nLCBhbmQgdXNpbmcgYWxwaGEgd2lsbCBoZWxwIHRvbwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9LjUsIHNpemU9LjUpCmBgYAoKT3RoZXIgb3B0aW9ucyBhcmUgdG8gZm9ybSAyZCBiaW5zLCBjb3VudCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoLCBhbmQgY29sb3IgaW4KcHJvcG9ydGlvbiB0byB0aGUgZnJlcXVlbmN5LgoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgPC0gZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpCnArIGdlb21fYmluMmQoKQpgYGAKCkkgcHJlZmVyIHRvIG1ha2UgbXkgY29sb3VyIHNjYWxlIGxpZ2h0IHRvIGRhcms6CmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIpCgpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYAoKVGhlIHNlY29uZCBwbG90IHRha2VzIGEgbG9nIG9mIHRoZSBjb3VudHMgc28geW91IGdldCBhIGJldHRlciBzcHJlYWQgb2YgY29sb3VycwoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgKyAgCiAgc3RhdF9iaW5oZXgoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYApgc3RhdF9iaW5oZXhgIHVzZXMgaGV4YWdvbmFsIGJpbm5pbmcKCiBBIG1vcmUgYWR2YW5jZWQgcGxvdCBmaXRzIGEgMmQgZGVuc2l0eSBlc3RpbWF0ZSBhbmQgZHJhdyB0aGlzIGFzIGEgY29udG91ciBwbG90LgogCmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifSAKcCArIHN0YXRfZGVuc2l0eTJkKCkKYGBgICAKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciIsIGV2YWw9Rn0gCiMgZmlsbHMgaW4gdGhlIGNvbnRvdXJzCnAgKyBzdGF0X2RlbnNpdHkyZChhZXMoZmlsbCA9IGFmdGVyX3N0YXQobGV2ZWwpKSwgZ2VvbSA9ICJwb2x5Z29uIikgCmBgYCAgICAKCiMjIFNjYXR0ZXJwbG90IG1hdHJpeAoKQmFjayB0byB0aGUgc21hbGxlciBkYXRhc2V0LgoKR28gdG8gYXJ0aWNsZXMgdGFiIGZvciBpbmZvCjxodHRwczovL2dnb2JpLmdpdGh1Yi5pby9nZ2FsbHkvPgoKCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShHR2FsbHkpCmdncGFpcnMoYmlrZSwgY29sdW1ucz1jKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiApKQpgYGAKCllvdSBjYW4gY2hhbmdlIHdoYXQgaXMgcGxvdHRlZCBvbiBsb3dlciwgdXBwZXIgYW5kIGRpYWdvbmFsLHVzaW5nCmBsb3dlcj1gIGV0Yy4KUHV0dGluZyBpbiBgbWFwcGluZz1gIGdpdmVzIGRlbnNpdGllcyBmb3IgYm90aCB5ZWFycyBzZXBhcmF0ZWx5CgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKICAKZ2dwYWlycyhiaWtlLCBtYXBwaW5nID0gYWVzKGNvbG9yID0geXIpLAogICAgICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiICksCiAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcCgic21vb3RoIiwgbWV0aG9kPSJsbSIsIHNlPUYsIGFscGhhPS41KSksCiAgZGlhZyA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImRlbnNpdHlEaWFnIiwgYWxwaGE9MC41ICkpKQoKYGBgCgoKYGdncGFpcnNgIGNhbiBoYW5kbGUgZmFjdG9ycyBhcyB3ZWxsLgpJbiB0aGlzIHBsb3QgYHlyc2AgaXMgaW4gYXMgYSBwbG90IHZhcmlhYmxlLCBhbmQgYXMgY29sb3VyLgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKCmdncGFpcnMoYmlrZSwgbWFwcGluZyA9IGFlcyhjb2xvciA9IHlyKSwKICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiLCJ5ciIpLAogICAgICAgIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gIHdyYXAoInNtb290aCIsIG1ldGhvZD0ibG0iLCBzZT1GLCBhbHBoYT0uNSkpLAogICAgICAgIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIsIGFscGhhPTAuNSApKSkKIApgYGAKCiMjIFBhcmFsbGVsIGNvb3JkaW5hdGVzCgpUaGVzZSBjYW4gYmUgY29uc3RydWN0ZWQgd2l0aCBmdW5jdGlvbiBgZ2dwYXJjb29yZGAgZnJvbSBHR2FsbHkuCgpCdXQsIGEgbmV3ZXIgcGFja2FnZSBjYWxsZWQgYGdncGNwYCBpcyBiZXR0ZXIuCgpgYGB7ciwgZXZhbD1GfQppbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJ5YXdlaWdlL2dncGNwIiwgYnVpbGRfdmlnbmV0dGVzID0gVFJVRSkKYGBgCgpDb25zdHJ1Y3Rpb246IHdpdGggYSBzYW1wbGUgb2YgMjAgYmlrZSBvYnNlcnZhdGlvbnMKCgoKCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciIsIGV2YWw9VCwgZWNobz1GfQoKcyA8LSBzYW1wbGUobnJvdyhiaWtlKSwyMCkKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0wLHNjYWxlPSJ1bmltaW5tYXgiKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0xLHNjYWxlPSJ1bmltaW5tYXgiKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIixzY2FsZT0idW5pbWlubWF4IikKYGBgCgpUaGlzIGlzIGhvdyB5b3UgY3JlYXRlIHRoZSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgaW4gYGdncGNwYAoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShnZ3BjcCkKZ2dwbG90KGJpa2VbcyxdLCBhZXMoY29sb3I9eXIpKSArIGdlb21fcGNwKGFlcyh2YXJzPXZhcnMoY2FzdWFsLHJlZ2lzdGVyZWQsIGNudCkpKQpgYGAKCkhlcmUsIGFsbCB2YXJpYWJsZXMgYXJlIHNjYWxlZCB0byB0aGUgdW5pdCBpbnRlcnZhbCBpbmRpdmlkdWFsbHkuClZhcmlhYmxlcyB0aGF0IGFyZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2lsbCBoYXZlIHBhcmFsbGVsIGxpbmUgc2VnbWVudHMsIGhlcmUgcmVnaXN0ZXJlZCBhbmQgY250LgoKT3RoZXIgc2NhbGluZyBvcHRpb25zIGFyZSBhdmFpbGFibGUgZWcKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChiaWtlW3MsXSwgYWVzKGNvbG9yPXlyKSkgKyBnZW9tX3BjcChhZXModmFycz12YXJzKGNhc3VhbCxyZWdpc3RlcmVkLCBjbnQpKSwgbWV0aG9kPSJyYXciKQoKYGBgCkluIHJhdywgbm8gc2NhbGluZyBpcyBkb25lLiBIZXJlIHlvdSBjYW4gc2VlIHRoYXQgbW9zdCB1c2VycyBwZXIgZGF5IGFyZSByZWdpc3RlcmVkLgoKVG8gc2VlIHdoYXQgY2VydGFpbiBwYXR0ZXJucyBsb29rIGxpa2UgaW4gYSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QsIGxldAp1cyBsb29rIGF0IHNvbWUgZmFrZSBkYXRhCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKeCA8LSBybm9ybSgxMDApCnkgPC0geCsgLjMqcm5vcm0oMTAwKQp6IDwtIC15KyAuMSpybm9ybSgxMDApCncgPC0gLXorIC41KnJub3JtKDEwMCkKZ2dwbG90KGRhdGEuZnJhbWUoeCx5LHosdykpKyBnZW9tX3BjcChhZXModmFycz12YXJzKGV2ZXJ5dGhpbmcoKSkpKQpgYGAKClBvc2l0aXZlIGNvcnJlbGF0aW9uIGV4aGliaXRzIGFzIHBhcmFsbGVsIHNlZ21lbnRzLgoKTmVnYXRpdmUgY29ycmVsYXRpb24gaGFzIGNyb3NzaW5nLiBUaGUgc3Ryb25nZXIgdGhlIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIHRoZSBzbWFsbGVyIHRoZSBwaW5jaCBwb2ludC4KCgoKCmBgYHtyLGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGJpa2UsIGFlcyhjb2xvcj13b3JraW5nZGF5KSkgKyBnZW9tX3BjcChhZXModmFycz12YXJzKGNhc3VhbCxyZWdpc3RlcmVkLCBjbnQpKSwgYWxwaGE9LjQpCgpgYGAKCkluIHRoZSBhYm92ZSB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gdGhlIGNhc3VhbC1yZWdpc3RlcmVkIGFzc29jaWF0aW9uIGZvciB3b3JraW5nZGF5IG9yIG5vdC4KQm90aCBncm91cHMgaGF2ZSBwb3NpdGl2ZSBhc3NvY2lhdGlvbiBidXQgc2xvcGVzIGFyZSBkaWZmZXJlbnQuClRoZXJlIGFyZSBtb3JlIGNhc3VhbCB1c2VycyBhdCB0aGUgd2Vla2VuZC4KClZlcmlmeWluZyB3aXRoIGEgc2NhdHRlcnBsb3QKCmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQpnZ3Bsb3QoZGF0YT1iaWtlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCwgY29sb3I9d29ya2luZ2RheSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKVGhlIHNhbWUgcGF0dGVybiB3YXMgZXZpZGVudCBpbiB0aGUgZnVsbCBiaWtlIGRhdGEuCgpTdGFuZGFyZCBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3RzIHJlcXVpcmUgbnVtZXJpYyB2YXJpYWJsZXMuIFRoZXJlIGFyZSB2YXJpYW50cyB0aGF0IGhhdmUgYmVlbiBkZWZpbmVkIGZvcgpjYXRlZ29yaWNhbCB2YXJpYWJsZXMuCgpXaXRoIGBnZ3BjcGAgZmFjdG9yIHZhcmlhYmxlcyBjYW4gYmUgdXNlZCBhcyBhbiBheGlzIHZhcmlhYmxlOgoKYGBge3IsZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQpnZ3Bsb3QoYmlrZSwgYWVzKHZhcnM9dmFycyggd29ya2luZ2RheSxjYXN1YWwscmVnaXN0ZXJlZCwgY250KSwgY29sb3I9d29ya2luZ2RheSkpICsgCiAgZ2VvbV9wY3AoYWxwaGE9LjQpCgpnZ3Bsb3QoYmlrZSwgYWVzKHZhcnM9dmFycyggd29ya2luZ2RheSxjYXN1YWwscmVnaXN0ZXJlZCwgY250KSwgY29sb3I9d29ya2luZ2RheSkpICsgCiAgIGdlb21fcGNwX2JveChib3h3aWR0aCA9IDAuMSwgZmlsbD0iZ3JleTcwIikrCiAgICBnZW9tX3BjcChhbHBoYT0uNCxib3h3aWR0aCA9IDAuMSkrCiAgIGdlb21fcGNwX3RleHQoYm94d2lkdGggPSAwLjEpCgoKYGBgCgpPdGhlciB2YXJpYW50cyBvZiBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3RzIGZvciBjYXRlZ29yaWNhbCBkYXRhIGFyZSBpbiBnZ2FsbHV2aWFsLCBnZ3BhcmFsbGVsLCBidXQgdGhlc2UgYXJlIGZvcgpmYWN0b3JzIG9ubHkuCgojIyBQcmFjdGljZSAzCgpVc2luZyB0aGUgYHBlbmd1aW5zYCBkYXRhc2V0LCBtYWtlIGEgcGxvdCBvZiAgdGhlIGZvdXIgZGltZW5zaW9uIHZhcmlhYmxlcywgY29sb3JlZCBieSBzcGVjaWVzLgoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GLCBldmFsPUZ9CmdncGFpcnMobmEub21pdChwZW5ndWlucyksIG1hcHBpbmcgPSBhZXMoY29sb3IgPSBzcGVjaWVzKSwKICAgICAgICAgICAgY29sdW1ucz1jKDM6NiksCiAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcCgic21vb3RoIiwgbWV0aG9kPSJsbSIsIHNlPUYsIGFscGhhPS41KSksCiAgZGlhZyA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImRlbnNpdHlEaWFnIiwgYWxwaGE9MC41ICkpKQpgYGAKV2hpY2ggcGFpciBvZiB2YXJpYWJsZXMgYW5kIHNwZWNpZXMgaGFzIHRoZSBoaWdoZXN0IGNvcnJlbGF0aW9uPwoKQ2FuIHlvdSBmaW5kIHR3byB2YXJpYWJsZXMgd2l0aCBuZWdhdGl2ZSBjb3JyZWxhdGlvbiwgYnV0IHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGhpbiBlYWNoIHNwZWNpZXM/ClRoaXMgaXMgY2FsbGVkIFNpbXBzb24ncyBwYXJhZG94LgoKVGhlbiBtYWtlIGEgcGFyYWxsZWwgY29vcmRpbmF0ZSBwbG90IG9mIHRoZSBzYW1lIGZvdXIgdmFyaWFibGVzLCB3aXRoIHRoZSBzYW1lIGNvbG91cmluZyBzY2hlbWUuCgpDYW4geW91IGlkZW50aWZ5IHByZXNlbmNlIG9mIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBjb3JyZWxhdGlvbj8KCkluIHRoaXMgcGxvdCwgaG93IGRvZXMgdGhlIEdlbnRvbyBzcGVjaWVzIGNvbXBhcmUgdG8gdGhlIG90aGVyIHR3byBzcGVjaWVzPwoKRmluYWxseSwgbWFrZSBhIHBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdCBvZiBzcGVjaWVzLCBpc2xhbmQsIGFuZCBib2R5X21hc3NfZywgY29sb3VyZWQgYnkgc3BlY2llcy4KSG93IG1hbnkgc3BlY2llcyBhcmUgb24gZWFjaCBpc2xhbmQ/CgpgYGB7cixmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciIsIGVjaG89RiwgZXZhbD1GfQoKZ2dwbG90KHBlbmd1aW5zLCBhZXModmFycz12YXJzKGJpbGxfbGVuZ3RoX21tOmJvZHlfbWFzc19nKSwgY29sb3I9c3BlY2llcykpKwogICAgICAgIGdlb21fcGNwKGFscGhhPS40KQoKCmdncGxvdChwZW5ndWlucywgYWVzKHZhcnM9dmFycyhzcGVjaWVzLCBpc2xhbmQsIHN0YXJ0c193aXRoKCJib2R5IikpLCBjb2xvcj1zcGVjaWVzKSkrCiAgZ2VvbV9wY3BfYm94KGJveHdpZHRoID0gMC4xLCBmaWxsPSJncmV5NzAiKSsKICBnZW9tX3BjcChhbHBoYT0uNCxib3h3aWR0aCA9IDAuMSkrCiAgZ2VvbV9wY3BfdGV4dChib3h3aWR0aCA9IDAuMSkKCmBgYAoKIyMgTWlzc2luZyBkYXRhCgpIZXJlIHdlIHVzZSBwYWNrYWdlIG5hbmlhci4KVGhpcyBpcyB0aGUgZGF0YXNldCBmb3IgeW91ciBhc3NpZ25tZW50LiBBbGwgdGhlIE5BcyBhcmUgbWFya2VkIGluIGdyZXkuCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciJ9Cm0gPC0gcmVhZF9jc3YoIm1hcmF0aG9uLmNzdiIsbmE9YygiIiwiICIsIk5BIikpCnZpc19taXNzKG0pCmBgYAoKQW4gdXBzZXQgcGxvdCBzaG93cyB0aGUgY29tYmluYXRpb24gb2YgbWlzc2luZyB2YWx1ZXMuCk1vc3Qgb2YgdGhlIE5BcyBpbnZvbHZlIHRoZSBjbHViIHZhcmlhYmxlLgoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIifQpnZ19taXNzX3Vwc2V0KG1bLC1jKDIyLDIwKV0pCmBgYAoKQ2hlY2sgb3V0IHRoZSB2aXN1YWxpc2F0aW9ucyBpbiBgbmFuaWFyYCBmb3IgbW9yZSBvcHRpb25zLCBlZyB0aGUgdmlnbmV0dGUgYXQgPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9uYW5pYXIvdmlnbmV0dGVzL25hbmlhci12aXN1YWxpc2F0aW9uLmh0bWw+CgoKIyMgdGl0YW5pYyBjYXNlIHN0dWR5CgpUaGVyZSB3ZXJlIDIyMDEgcGVvcGxlIGFib2FyZCB0aGUgdGl0YW5pYyB3aGVuIGl0IHNhbmsgaW4gMTkxMi4KQSBzdW1tYXJ5IG9mIHRoZSBwZW9wbGUgb24gYm9hcmQgYW5kIHdoZXRoZXIgdGhleSBzdXJ2aXZlZCBvciBub3QgaXMgZ2l2ZW4gaW4gdGhlIHRpdGFuaWNhbmljIGRhdGFzZXQuCgpIZXJlIGFyZSBzb21lIGltYWdlcyBhcyBpdCBsZWZ0IGl0cyBsYXN0IHBvcnQgb2YgY2FsbCwgQ29iaC4KCmBgYHtyLCBlY2hvPUYsIGV2YWw9VCwgIG91dC53aWR0aD0iMzAlIixmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5zaG93PSdob2xkJ30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoYygiZmlncy90aXRhbmljLnBuZyIpKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhjKCJmaWdzL2NvYmgucG5nIikpCmBgYAoKCkNhbiB3ZSBmaWd1cmUgb3V0IGZyb20gdGhpcyBkYXRhIHdoaWNoIGNhdGVnb3J5IG9mIHBhc3NlbmdlciBoYWQgdGhlIGhpZ2hlc3QgY2hhbmNlIG9mIHN1cnZpdmFsPwoKVGhpcyBjYXNlIHN0dWR5IGxvb2tzIGF0IDQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHNpbXVsdGFuZW91c2x5OgotIFN1cnZpdmFsICh5ZXMgb3Igbm8pLCAKLSBjbGFzcyAoZmlyc3QsIHNlY29uZCwgdGhpcmQsIGNyZXcsKQotIGdlbmRlciAobWFsZSBvciBmZW1hbGUpIGFuZCAKLSBhZ2UgKGNoaWxkIG9yIGFkdWx0KS4KCgpGaXJzdCB3ZSBsb29rIGF0IHRoZSBudW1iZXIgb2Ygc3Vydml2b3JzIGJ5IGNsYXNzCmBgYHtyLCAgZmlnLndpZHRoPTQuNSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CiN0aXRhbmljYW5pYyAgIyBhIDRkIGFycmF5Li4uLgp0aXRhbmljIDwtIGFzLmRhdGEuZnJhbWUoVGl0YW5pYykKaGVhZCh0aXRhbmljKQp0aXRhbmljPC0gdGl0YW5pY1tjKDE3OjMyLDE6MTYpLF0KCmdncGxvdChkYXRhID0gdGl0YW5pYywgYWVzKHggPSBDbGFzcywgeSA9IEZyZXEsZmlsbD1TdXJ2aXZlZCkpICsKICBnZW9tX2NvbCgpIApgYGAKCmFuZCB0aGVuIHRoZSBzdXJ2aXZhbCByYXRlcyBieSBjbGFzcwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoeCA9IENsYXNzLCB5ID0gRnJlcSxmaWxsPVN1cnZpdmVkKSkgKwogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikgCmBgYAoKCi0gVGhlcmUgaXMgYSBiaWcgZGlmZmVyZW5jZSBpbiBTdXJ2aXZhbCByYXRlcyBhY3Jvc3MgdGhlIGNsYXNzZXMuCi0gRmlyc3QgY2xhc3MgcGFzc2VuZ2VycyBoYXZlIHRoZSBoaWdoZXN0IGNoYW5jZSBvZiBzdXJ2aXZhbCwgY3JldyBtZW1iZXJzIHRoZSBsb3dlc3QuIAoKCldlIGNhbiBicmluZyBpbiBnZW5kZXIgd2l0aCBgZmFjZXRfd3JhcGAKCmBgYHtyLCAgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChkYXRhID0gdGl0YW5pYywgYWVzKHggPSBDbGFzcywgeSA9IEZyZXEsZmlsbD1TdXJ2aXZlZCkpICsKICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpICsgCiAgZmFjZXRfd3JhcCggfiBTZXgpICsgeWxhYigiUmF0ZSIpCmBgYAoKLSBUaGlyZCBjbGFzcyBmZW1hbGVzIGRvIHdvcnNlIHRoYW4gY3JldyBmZW1hbGVzICg0NiUgdmVyc3VzIDg3JSksIGFuZAp0aGlyZCBjbGFzcyBtYWxlcyBkbyB3b3JzZSB0aGFuIGNyZXcgbWFsZXMgKDE3JSB2ZXJzdXMgMjIlKSwgYWx0aG91Z2ggd2hlbiBnZW5kZXIgaXMgaWdub3JlZCB0aGUgc2l0dWF0aW9uIGlzCnJldmVyc2VkLgoKLSBUaGlzIGFwcGFyZW50IHBhcmFkb3ggKFNpbXBzb24ncyBwYXJhZG94KSBvY2N1cnMgYmVjYXVzZSB0aGUgdmFzdCBtYWpvcml0eSBvZiBjcmV3IG1lbWJlcnMgYXJlIG1hbGUuCgotIFRoZSBvdmVyYWxsIDNyZCBjbGFzcyBzdXJ2aXZhbCByYXRlIG9mIDI1JSBpcyBhbiBhdmVyYWdlIG9mIDQ2JSBhbmQgMTclLCBidXQgYXMgdGhlcmUgYXJlIDIuNiB0aW1lcwptb3JlIG1lbiB0aGFuIHdvbWVuIGluIDNyZCBjbGFzcyAxNyUgZ2V0cyBtb3JlIHdlaWdodC4KCi0gVGhlIG92ZXJhbGwgY3JldyBzdXJ2aXZhbCByYXRlIG9mIDI0JSBpcyBhbiBhdmVyYWdlIG9mIDg3JSBhbmQgMjJcJSwgYnV0IGFzIHRoZXJlIGFyZSAzNyB0aW1lcwptb3JlIG1lbiB0aGFuIHdvbWVuIGluIGNyZXcgY2xhc3MgODclIGdldHMgdmVyeSBsaXR0bGUgd2VpZ2h0LgoKRmluYWxseSwgYnJpbmdpbmcgaW4gYWdlCmBgYHtyLCAgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD00LjUsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoeCA9IENsYXNzLCB5ID0gRnJlcSxmaWxsPVN1cnZpdmVkKSkgKwogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikgKyAKICBmYWNldF93cmFwKCB+IEFnZSsgU2V4KSArIHlsYWIoIlJhdGUiKQpgYGAKCldlIGNhbiBnZXQgc29tZSBzaW1wbGlmaWNhdGlvbiBieSBzaG93aW5nIHRoZSBzdXJ2aXZhbCByYXRlLCB0aGF0IGlzIG9taXQgdGhlIHJlZCBzZWN0aW9ucy4KCgpgYGB7ciwgIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NC41LCBmaWcuYWxpZ249ImNlbnRlciJ9CnRpdGFuaWNSIDwtIAp0aXRhbmljICU+JSAgZ3JvdXBfYnkoQ2xhc3MsIFNleCxBZ2UpICU+JQpzdW1tYXJpc2UoZGllZD1GcmVxWzJdLCBzdXJ2aXZlZD1GcmVxWzFdLCByYXRlPXN1cnZpdmVkL3N1bShGcmVxKSkKdGl0YW5pY1IKCmdncGxvdChkYXRhID0gdGl0YW5pY1IsIGFlcyh4ID0gQ2xhc3MsIHkgPSByYXRlKSkgKwogIGdlb21fY29sKGZpbGw9ImxpZ2h0Ymx1ZSIpICsgCiAgZmFjZXRfd3JhcCggfiBBZ2UrIFNleCkgCgoKICAKYGBgCgpPbmUgb2YgdGhlIGV4YW1wbGVzIGluIHRoZSBgZ2dwY3BgIHZpZ25ldHRlIGhhcyBhIHBhcmFsbGVsIGNvb3JkaW5hdGVzIHZlcnNpb24gb2YgdGhpcyBkYXRhOgo8aHR0cHM6Ly9naXRodWIuY29tL3lhd2VpZ2UvZ2dwY3A+ICAKCgpgYGB7ciwgZWNobz1GLCBldmFsPVQsICBvdXQud2lkdGg9IjUwJSIsZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuc2hvdz0naG9sZCd9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoImZpZ3MvcGNwLnBuZyIpKQpgYGAKCiMjIFByYWN0aWNlIDQKCkV4cGxvcmUgdGhlIHN1cnZpdmFsIHJhdGVzIGZvciB0aGUgZGF0YSBgbHVzaXRhbmlhLmNzdmAgb24gPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgpUaGlzIGlzIGEgdGFidWxhdGVkIHZlcnNpb24gb2YgdGhlIGRhdGEsIGluIHRoZSBzYW1lIGZvcm1hdCBhcyBgdGl0YW5pY2AuIFRoZSBjb21wbGV0ZSBkYXRhc2V0CmlzIGF2YWlsYWJsZSBvbiBLYWdnbGUuCgpZb3UgY2FuIGZpbmQgb3V0IG1vcmUgYWJvdXQgdGhlIHNoaXB3cmVjayA8aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUk1TX0x1c2l0YW5pYT4KCgoKCgpgYGB7ciwgZWNobz1GQUxTRSwgZXZhbD1GQUxTRSwgcHVybD1GQUxTRX0Ka25pdHI6OnB1cmwoImxlY3QyLlJtZCIpCgpgYGAKCgo=